#include "stdio.h"
#include "string.h"
#include <time.h>
#include "pthread.h"
#include "unistd.h"
#include <stdlib.h>

#include "mytimer.h"

int gettimeofday(struct timeval * tp, void * tzp);

int
timer_init(timer_queue *queue, timer_expiry *cb, time_t interval, pthread_mutex_t *mymutex)
{
    pthread_t pid;
    memset(queue, 0, sizeof(timer_queue));
    if ((queue->mycond = malloc(sizeof(pthread_cond_t))) == NULL)     return 0;
    if ((queue->mymutex = mymutex) == NULL)       return 0;
    //if (pthread_mutex_init(queue->mymutex, NULL))       return 0;
    if (pthread_cond_init(queue->mycond, NULL))        return 0;
    queue->cb = cb;
    queue->interval = interval;
    pthread_create(&pid,NULL,&timer_thread,(void *)queue);
    pthread_detach(pid);
    return 1;
}

struct timer *
            add_timer(timer_queue *queue, void *user_data)
{
    struct timer *t, *h;
    pthread_cond_t *mycond;
    // pthread_mutex_t *mymutex;

    t = malloc(sizeof (struct timer));
    if (t == NULL) return 0;
    memset(t, 0 ,sizeof(struct timer));
    t->user_data = user_data;
    gettimeofday(&t->expire_time, NULL);
    t->expire_time.tv_sec += queue->interval;
    t->queue = queue;

    mycond = queue->mycond;
    //mymutex = queue->mymutex;
    // pthread_mutex_lock(mymutex);
    h = queue->timer_header;
    if (h)
    {
        h = queue->timer_tail;
        h->next = t;
        t->prev = h;
        t->next = NULL;
        queue->timer_tail = t;
        //     pthread_mutex_unlock(mymutex);
    }
    else
    {
        queue->timer_tail = t;
        queue->timer_header = t;
        t->prev = NULL;
        t->next = NULL;
        //      pthread_mutex_unlock(mymutex);
        pthread_cond_signal(mycond);
    }
    return t;
}


int
del_timer(struct timer *t)
{
    timer_queue *queue;
    pthread_cond_t *mycond;
    // pthread_mutex_t *mymutex;

    if (t == NULL) return 1;
    queue = t->queue;
    mycond = queue->mycond;
    //  mymutex = queue->mymutex;
    //  pthread_mutex_lock(mymutex);
    if (t->next)
    {
        t->next->prev = t->prev;
    }
    else       // t is the tail
    {
        queue->timer_tail = t->prev;
    }
    if (t->prev)
    {
        t->prev->next = t->next;
        free(t);
        //      pthread_mutex_unlock(mymutex);
    }
    else                              // t is the header
    {
        queue->timer_header = t->next;
        free(t);
        //    pthread_mutex_unlock(mymutex);
        pthread_cond_signal(mycond);
    }
    return 1;
}


#define TIMEVAL_EXPIRE(a, now)  (((a).tv_sec > now.tv_sec) || (((a).tv_sec == now.tv_sec) && ((a).tv_usec > now.tv_usec)))

void *
timer_thread(void *arg)
{
    timer_queue *timer;
    pthread_cond_t *mycond;
    pthread_mutex_t *mymutex;
    struct timespec mytime;
    struct timer *t, *h;
    int ret;

    timer = (timer_queue *)arg;
    mycond = timer->mycond;
    mymutex = timer->mymutex;
    pthread_mutex_lock(mymutex);
    while (1)
    {
        if (timer->timer_header == NULL)
        {
            pthread_cond_wait(mycond, mymutex);
            continue;
        }
        else
        {
            struct timeval now;
            gettimeofday(&now, NULL);
            if (TIMEVAL_EXPIRE(timer->timer_header->expire_time, now))
            {
                mytime.tv_sec = timer->timer_header->expire_time.tv_sec;
                mytime.tv_nsec = timer->timer_header->expire_time.tv_usec * 1000;
                //printf("wait %d\n", timer->timer_header->expire_time - time(NULL));
                ret = pthread_cond_timedwait(mycond, mymutex, (const struct timespec *)&mytime);
                continue;
            }
        }
        // timer expired
        t = timer->timer_header;
        if (timer->cb(t->user_data) == 1)                      // del timer
        {
            timer->timer_header = t->next;
            if (t->next) t->next->prev = NULL;
            free(t);
        }
        else                       // renew timer
        {
            gettimeofday(&t->expire_time, NULL);
            t->expire_time.tv_sec += timer->interval;
            if (t->next)
            {
                timer->timer_header = t->next;
                t->next->prev = NULL;
                h = timer->timer_tail;
                h->next = t;
                t->prev = h;
                t->next = NULL;
                timer->timer_tail = t;
            }
        }

    }
    return NULL;
}

/*
int
dealer(void *user_data) {
    printf("%s expired\n", (char *)user_data);
}


void
main() {
    timer_queue queue;
    struct timer *t, *t1;
    char i[][10] = {"timer 1" , "timer 2" , "timer 3"};
    char o[90];

    timer_init(&queue, dealer, 2);


    t = add_timer(&queue, (void *)i[0]);
    sleep(1);
    add_timer(&queue, (void *)i[1]);
    t1 = add_timer(&queue, (void *)i[2]);
    del_timer(&queue, t);
    del_timer(&queue, t1);
    sleep(5);
    add_timer(&queue, (void *)i[0]);
    sleep(10);
}*/





